/*
 * @Date: 2022-03-25 12:08:02
 * @LastEditors: 春贰
 * @gitee: https://gitee.com/chun22222222
 * @github: https://github.com/chun222
 * @Desc:
 * @LastEditTime: 2022-08-07 14:55:31
 * @FilePath: \server\system\core\ws\client.go
 */
// Copyright 2013 The Gorilla WebSocket Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package ws

import (
	"encoding/json"
	"fmt"
	"log"
	"net/http"
	"time"

	"github.com/gorilla/websocket"
)

const (
	// Time allowed to write a message to the peer.
	writeWait = 10 * time.Second

	// Time allowed to read the next pong message from the peer.
	pongWait = 60 * time.Second

	// Send pings to peer with this period. Must be less than pongWait.
	pingPeriod = (pongWait * 9) / 10

	// Maximum message size allowed from peer.
	maxMessageSize = 1024
)

type User struct {
	Id        int    `json:"id"`
	LoginName string `json:"login_name"`
	UserName  string `json:"user_name"`
	Phone     string `json:"phone"`
	Email     string `json:"email"`
}

type UserData struct {
	Ip       string `json:"ip"`
	UserInfo User   `json:"userinfo"`
	Type     string `json:"type"`
	Content  string `json:"content"`
	UserList []int  `json:"user_list"`
}

var (
	newline = []byte{'\n'}
	space   = []byte{' '}
)

var upgrader = websocket.Upgrader{
	ReadBufferSize:  1024,
	WriteBufferSize: 1024,
	CheckOrigin: func(r *http.Request) bool {
		return true
	},
}

// Client is a middleman between the websocket connection and the hub.
type Client struct {
	hub *Hub

	// The websocket connection.
	conn *websocket.Conn

	// Buffered channel of outbound messages.
	Send chan []byte

	//数据
	Userdata *UserData
}

// readPump pumps messages from the websocket connection to the hub.
//
// The application runs readPump in a per-connection goroutine. The application
// ensures that there is at most one reader on a connection by executing all
// reads from this goroutine.
func (c *Client) readPump() {
	//避免影响主程序
	defer func() {
		if err := recover(); err != nil {
			fmt.Println("readPump发生错误", err)
		}
	}()
	defer func() {
		//断开连接
		//fmt.Println("下线==============", c.hub.clients)
		c.Userdata.Type = TypeOffline
		c.Userdata.Content = "-"
		c.Userdata.UserList = OnlineIds()
		data_b, _ := json.Marshal(c.Userdata)
		c.hub.Broadcast <- data_b //广播下线
		c.hub.unregister <- c
		c.conn.Close()
	}()
	c.conn.SetReadLimit(maxMessageSize)
	c.conn.SetReadDeadline(time.Now().Add(pongWait))
	c.conn.SetPongHandler(func(string) error { c.conn.SetReadDeadline(time.Now().Add(pongWait)); return nil })
	for {
		_, message, err := c.conn.ReadMessage()
		if err != nil {
			if websocket.IsUnexpectedCloseError(err, websocket.CloseGoingAway, websocket.CloseAbnormalClosure) {
				log.Printf("error: %v", err)
			}
			break
		}
		json.Unmarshal(message, &c.Userdata)
		c.reader(message)
		//	message = bytes.TrimSpace(bytes.Replace(message, newline, space, -1))

	}
}

//解析传入信息
func (c *Client) reader(message []byte) {

	json.Unmarshal(message, &c.Userdata)
	switch c.Userdata.Type {
	case TypeOnline:
		//上线 广播上线用户信息
		//fmt.Println("shangxian==============", c.hub.clients)
		user_list := OnlineIds()

		c.Userdata.UserList = user_list
		c.Userdata.Type = TypeOnline
		data_b, _ := json.Marshal(c.Userdata)
		c.hub.Broadcast <- data_b

	default:
		fmt.Print("========default================")
	}
}

// writePump pumps messages from the hub to the websocket connection.
//
// A goroutine running writePump is started for each connection. The
// application ensures that there is at most one writer to a connection by
// executing all writes from this goroutine.
//写入消息
func (c *Client) writePump() {
	ticker := time.NewTicker(pingPeriod)
	defer func() {
		ticker.Stop()
		c.conn.Close()
	}()
	//避免影响主程序
	defer func() {
		if err := recover(); err != nil {
			fmt.Println("writePump发生错误", err)
		}
	}()
	for {
		select {
		case message, ok := <-c.Send:
			c.conn.SetWriteDeadline(time.Now().Add(writeWait))
			if !ok {
				// The hub closed the channel.
				c.conn.WriteMessage(websocket.CloseMessage, []byte{})
				return
			}

			w, err := c.conn.NextWriter(websocket.TextMessage)
			if err != nil {
				return
			}
			w.Write(message)

			// Add queued chat messages to the current websocket message.
			n := len(c.Send)
			for i := 0; i < n; i++ {
				w.Write(newline)
				w.Write(<-c.Send)
			}

			if err := w.Close(); err != nil {
				return
			}
		case <-ticker.C:
			c.conn.SetWriteDeadline(time.Now().Add(writeWait))
			if err := c.conn.WriteMessage(websocket.PingMessage, nil); err != nil {
				return
			}
		}
	}
}

// serveWs handles websocket requests from the peer.
func serveWs(hub *Hub, w http.ResponseWriter, r *http.Request) {
	conn, err := upgrader.Upgrade(w, r, nil)
	if err != nil {
		log.Println(err)
		return
	}
	client := &Client{hub: hub, conn: conn, Send: make(chan []byte, 256)}
	client.hub.register <- client

	// Allow collection of memory referenced by the caller by doing all work in
	// new goroutines.
	go client.writePump()
	go client.readPump()
}
